import { Action, ActionType, Column, Cross, Declared, Habtm, Property, Relation, RelationType, SearchColumn, Seed, TableColumn, Element } from "../types"
import { camelCase, snakeCase, lowerFirst } from 'lodash-es'
import { formatSearch, getOpByColumn, getValidates, toTableColumn } from "./ColumnUtil"
import { cloneDeep } from 'lodash-es'

// export function initSeeds(seeds: Record<string, Seed>, libNames: string[], app: App, micro?: Micro) {
//   const libSeeds = libManager.getLibSeeds(libNames);
//   if(!micro) { // 主服务下引入uaa库
//     for(const seedName in uaa.seeds) libSeeds[seedName] = uaa.seeds[seedName]
//   }

//   const ret = merge(seeds, libSeeds);
//   for(const seedName in ret) {
//     const seed = ret[seedName];
//     if(!seed.type) seed.type = 'module'
    
//     seed.name = seedName
//     if(seed.stats) {
//       for(const statName in seed.stats) seed.stats[statName].name = statName
//     }
//     initActions(seed.actions, seed, app, micro)
//     initColumns(seed, app, micro)
//     initRelations(seed, ret)
//   }
  
//   return ret
// }

// export function initActions(actions: Record<string, Action>, seed: Seed, app: App, micro?: Micro) {
//   if(!actions) return

//   for(const actionName in actions) {
//     const action = actions[actionName]
//     action.origin = 'seed'
//     if(action.stats) {
//       for(const statName in action.stats) action.stats[statName].name = statName
//     }
//     initAction(action, actionName, seed, app, micro)
//   }
// }

// export function initAction(action: Action, actionName: string, seed?: Seed, app?: App, micro?: Micro) {
//   action.name = actionName
//   action.seed = seed
//   action.micro = micro
//   action.app = app
//   // if(!action.type) return

//   // switch(action.type) {
//   //   case 'save':
//   //   case 'remove':
//   //   case 'reset':
//   //     action.set = fillAction(action, action.set);
//   //     break;
//   //   case 'update':
//   //   case 'set':
//   //     action.get = fillAction(action, action.get);
//   //     action.set = fillAction(action, action.set);
//   //     break;
//   //   default: action.get = fillAction(action, action.get);
//   // }
// }

// function fillAction(defaultAction: Action, geterOrSeter?: Action) {
//   const ret = geterOrSeter || {}
//   ret.name = ret.name || defaultAction.name
//   ret.position = ret.position || defaultAction.position
//   ret.component = ret.component || defaultAction.component
//   ret.componentProps = ret.componentProps || defaultAction.componentProps
//   ret.label = ret.label || defaultAction.label
//   ret.comment = ret.comment || defaultAction.comment
//   ret.columnName = ret.columnName || defaultAction.columnName
//   ret.columnValue = ret.columnValue || defaultAction.columnValue
//   ret.columns = ret.columns || defaultAction.columns

//   ret.declares = ret.declares || defaultAction.declares
//   ret.searches = ret.searches || defaultAction.searches
//   ret.orders = ret.orders || defaultAction.orders
//   ret.stats = ret.stats || defaultAction.stats

//   ret.checkLogin = ret.checkLogin == undefined ? defaultAction.checkLogin : ret.checkLogin
//   ret.checkAction = ret.checkAction == undefined ? defaultAction.checkAction : ret.checkAction
//   ret.useKeyword = ret.useKeyword == undefined ? defaultAction.useKeyword : ret.useKeyword

//   ret.onClick = ret.onClick || defaultAction.onClick
  
//   ret.origin = defaultAction.origin
//   ret.seed = defaultAction.seed
//   ret.micro = defaultAction.micro
//   ret.app = defaultAction.app

//   return ret
// }

// function initColumns(seed: Seed, app: App, micro?: Micro) {
//   const columns = seed.columns
//     if(!columns) return
    
//     for(const columnName in columns) {
//       const column = columns[columnName]
//       column.name = columnName
//       column.seed = seed
//       column.micro = micro
//       column.app = app

//       if(column.type == 'code') column.required = true
//     }
// }

// function initRelations(seed: Seed, seeds: Record<string, Seed>) {
//   if(seed.belongsTo) {
//     for(const cross in seed.belongsTo) {
//       const belongsTo = seed.belongsTo[cross]
//       if(!belongsTo.selfSeed) belongsTo.selfSeed = seed

//       if(!belongsTo.targetSeed) {
//         const targetSeed = seeds[belongsTo.seedName]
//         if(!targetSeed) throw new Error(`元数据【${belongsTo.seedName}】不存在`);

//         belongsTo.targetSeed = targetSeed
//       }
//     }
//   }

//   if(seed.hasOne) {
//     for(const cross in seed.hasOne) {
//       const hasOne = seed.hasOne[cross]
//       if(!hasOne.selfSeed) hasOne.selfSeed = seed

//       if(!hasOne.targetSeed) {
//         const targetSeed = seeds[hasOne.seedName]
//         if(!targetSeed) throw new Error(`元数据【${hasOne.seedName}】不存在`);
        
//         hasOne.targetSeed = targetSeed
//       }
//     }
//   }

//   if(seed.hasMany) {
//     for(const cross in seed.hasMany) {
//       const hasMany = seed.hasMany[cross]
//       if(!hasMany.selfSeed) hasMany.selfSeed = seed

//       if(!hasMany.targetSeed) {
//         const targetSeed = seeds[hasMany.seedName]
//         if(!targetSeed) throw new Error(`元数据【${hasMany.seedName}】不存在`);
        
//         hasMany.targetSeed = targetSeed
//       }
//     }
//   }

//   if(seed.habtm) {
//     for(const cross in seed.habtm) {
//       const habtm = seed.habtm[cross]
//       if(!habtm.selfSeed) habtm.selfSeed = seed

//       if(!habtm.targetSeed) {
//         const targetSeed = seeds[habtm.seedName]
//         if(!targetSeed) throw new Error(`元数据【${habtm.seedName}】不存在`);
        
//         habtm.targetSeed = targetSeed
//       }

//       if(!habtm.throughSeed) {
//         if(!habtm.throughSeedName) throw Error(`请为【${seed.name}.${cross}】配置【throughSeedName】`)
//         const throughSeed = seeds[habtm.throughSeedName];
//         if(!throughSeed) throw new Error(`元数据【${habtm.throughSeedName}】不存在`);
        
//         habtm.throughSeed = throughSeed;
//       }
//     }
//   }
// }

// function merge(seeds: Record<string, Seed>, libSeeds: Record<string, Seed>) {
//     const ret: Record<string, Seed> = {};
//     const seedNames = Array.from(new Set([...Object.keys(seeds || {}), ...Object.keys(libSeeds || {})]))

//     for(const seedName of seedNames) {
//       const seed = mergeOne(seeds?.[seedName], libSeeds?.[seedName]);
//       seed.name = seedName
//       ret[seedName] = seed
//     }

//     return ret;
// }

// function mergeOne(seed: Seed, libSeed: Seed) {
//   if(!seed && !libSeed) throw new Error("合并的数据不能为空");

//   if(!seed) return cloneSeed(libSeed)
//   if(!libSeed) return seed;

//   const ret = cloneSeed(libSeed);
//   mergeSeed(ret, seed);

//   return ret;
// }

// export function seedToRoutes(seeds: Record<string, Seed>, micro?: Micro): RouteRecordRaw[] {
//   const ret: RouteRecordRaw[] = []

//   for(const seedName in seeds) {
//     const seed = seeds[seedName]
//     const { type, component, layout, componentProps, affix, hidden, breadcrumb, noTagsView, position, icon, label } = seed
//     if(type != 'module') continue

//     const props = { ...(componentProps || {}), seedName, microName: micro?.name }
//     ret.push({
//       name: micro ? `${micro.name}/${seedName}` : seedName,
//       path: seedName,
//       component: layout ? Container : getComponent('seed', component),
//       props: layout ? { layout, props } : props,
//       meta: {
//         affix: affix == undefined ? false : affix,
//         hidden: hidden == undefined ? false : hidden,
//         breadcrumb: breadcrumb == undefined ? true : breadcrumb,
//         noTagsView: noTagsView == undefined ? false : noTagsView,
//         position: position || 'menu',
//         mode: seed.mode,
//         icon: icon || 'bxs:home',
//         label: `${label}管理`
//       }
//     })
//   }

//   return ret
// }

export function getRawColumn(seed: Seed, declare: string): Column {
  const { columns } = seed
  if(columns[declare]) return cloneDeep(columns[declare])

  return cloneDeep(getCrossColumn(seed, declare))
}

export function getTableColumns(action: Action) {console.log('action====', action)
  const seed = action.seed
  const declares = getDeclares(action)

  const ret: TableColumn[] = []
  for(const declare of declares) {
    let column = getRawColumn(seed, declare)

    const declareColumn = action.columns?.[declare]
    if(declareColumn) {
      column.label = declareColumn.label || column.label
      column.element = declareColumn.element || column.element
      column.elementProps = declareColumn.elementProps || column.elementProps
    }
    ret.push(toTableColumn(column))
  }

  return ret
}

export function getSearchColumns(action: Action) {
  // const actions = seed.actions
  // if(!actions) throw new Error(`${seed.name}中不存在actions`)

  // const action = actions[actionName]
  // if(!action) throw new Error(`${seed.name}中不存在action【${actionName}】`)

  let searches = getSearches(action)
  if(searches.length == 0) return {}
  
  const ret: Record<string, SearchColumn> = {}
  const seed = action.seed
  const { columns } = seed
  for(const i in searches) {
    const search = searches[i]
    let [fullColumn, op] = formatSearch(search)
    let column: SearchColumn
    if(fullColumn.includes('.')) {
      column = getCrossColumn(seed, fullColumn)
    } else {
      column = columns[fullColumn]
      if(!column) column = getCrossColumn(seed, fullColumn)
    }

    if(!op) op = getOpByColumn(column)
    column.op = op
    ret[search] = column
  }

  return ret
}

export function getCrossColumn(seed: Seed, fullCross: string): Column {
  let cross = fullCross
  let subFullColumn = undefined
  if(fullCross.includes('.')) {
    const pos = fullCross.indexOf('.')
    cross = fullCross.substring(0, pos)
    subFullColumn = fullCross.substring(pos + 1)
  }

  const relation = getRelation(seed, cross)
  return getCrossDeepColumn(camelCase(cross), relation, subFullColumn)
}

export function getCrossDeepColumn(parentPath: string, parentRelation: Relation, fullColumn: string) {
  const seed = parentRelation.targetSeed
  const parentLabel = parentRelation.label || parentRelation.comment
  if(!fullColumn) {
    let columns = seed.columns
    const nameColumn = snakeCase(seed.nameColumn)
    if(nameColumn) {
      if(!columns[nameColumn]) throw new Error(`【${seed.name}】下【nameColumn】配置错误，列【${nameColumn}】不存在`)

      return { ...columns[nameColumn], name: `${parentPath}.${nameColumn}`, label: `${parentLabel}${columns[nameColumn].label}` }
    }
    
    if(columns['name']) return { ...columns['name'], name: `${parentPath}.name`, label: `${parentLabel}${columns['name'].label}` }
    if(columns['id']) return { ...columns['id'], name: `${parentPath}.id`, label: `${parentLabel}${columns['id'].label}` }

    throw new Error(`请为【${seed.name}】配置正确的【nameColumn】`)
  }

  if(fullColumn.includes('.')) {
    const pos = fullColumn.indexOf('.')
    const cross = fullColumn.substring(0, pos)
    const subFullColumn = fullColumn.substring(pos + 1)
    const relation = getRelation(seed, cross)
    return getCrossDeepColumn(parentPath + '.' + camelCase(cross), relation, subFullColumn)
  } else {
    const { columns } = seed
    if(columns[fullColumn]) return { ...columns[fullColumn], name: `${parentPath}.${fullColumn}`, label: `${parentLabel}${columns[fullColumn].label}` }

    const relation = getRelation(seed, fullColumn)
    return getCrossDeepColumn(parentPath + '.' + camelCase(fullColumn), relation, undefined)
  }
}

export function getNameColumn(seed: Seed) {
  const nameColumn = seed.nameColumn
  const columns = seed.columns || {}
  if(nameColumn) {
    if(columns[snakeCase(nameColumn)]) return nameColumn

    throw Error(`【${seed.name}】下的【nameColumn】【${nameColumn}】不存在`)
  }

  if(columns['name']) return 'name'

  throw Error(`请为【${seed.name}】配置【nameColumn】`)
}

export function getRelation(seed: Seed, cross: string) {
  cross = snakeCase(cross)
  const { hasOne, belongsTo, hasMany, habtm } = seed
  if(hasOne && hasOne[cross]) return hasOne[cross]
  if(belongsTo && belongsTo[cross]) return belongsTo[cross]
  if(hasMany && hasMany[cross]) return hasMany[cross]
  if(habtm && habtm[cross]) return habtm[cross]

  throw new Error(`【${seed.name}】下的关系【${cross}】不存在`)
}

export function getRelations(seed: Seed) {
  const ret: Record<string, Relation> = {}

  const { hasOne, belongsTo, hasMany, habtm } = seed
  if(hasOne) for(const cross in hasOne) ret[cross] = hasOne[cross]
  if(belongsTo) for(const cross in belongsTo) ret[cross] = belongsTo[cross]
  if(hasMany) for(const cross in hasMany) ret[cross] = hasMany[cross]
  if(habtm) for(const cross in habtm) ret[cross] = habtm[cross]

  return ret
}


export function getInitData(seed: Seed, declared: Declared): Record<string, any> {
  const ret: Record<string, any> = {}

  const { columns, hasOne, hasMany } = seed
  for(const columnOrCross in declared) {
    if('id' === columnOrCross) continue

    const value = declared[columnOrCross]
    if(value) {      
      if(hasOne && hasOne[columnOrCross]) {
        ret[camelCase(columnOrCross)] = getInitData(hasOne[columnOrCross].targetSeed, value)
      }

      if(hasMany && hasMany[columnOrCross]) {
        ret[camelCase(columnOrCross)] = []
      }
    } else {
      const column = columns[snakeCase(columnOrCross)]
      const defVal = column.defVal
      if(defVal) ret[camelCase(column.name)] = defVal
    }
  }

  return ret
}

export function initData(data: Record<string, any>, columns: Record<string, Column>, crosses: Record<string, Cross>) {
  delete data.id
  for(const i in columns) {
    const column = columns[i]
    if(column.defVal) data[camelCase(column.name)] = column.defVal
  }

  for(const cross in crosses) {
    const key = camelCase(cross)
    const relation = crosses[cross]
    if(relation.type == 'hasOne') {
      const one = {}
      for(const i in relation.columns) {
        const column = relation.columns[i]
        if(column.defVal) one[camelCase(column.name)] = column.defVal
      }

      data[key] = initData(one, relation.columns, relation.crosses)
    } else {
      data[key] = []
    }
  }

  return data
}

// export function getColumns(seed: Seed, actionName: Action | string) {
//   const ret: Record<string, Column> = {}
//   const declares = getDeclares(seed, actionName)

//   const declared = buildDeclared(seed, declares)
//   for(const columnOrCross in declared) {
//     const value = declared[columnOrCross]
//     if(!value) {
//       const column = seed.columns[snakeCase(columnOrCross)]
//       ret[column.name] = column
//     }
//   }
  
//   return ret
// }

export function getCross(seed: Seed, declared: Declared, actionElements?: Record<string, Element>): Cross {
  const ret: Cross = {columns: {}, rules: {}, crosses: {}}
  for(const columnOrCross in declared) {
    const value = declared[columnOrCross]
    if(value) {
      const { hasOne, belongsTo, hasMany, habtm } = seed
      if(hasOne && hasOne[columnOrCross]) {
        const relation = hasOne[columnOrCross]
        const targetSeed = relation.targetSeed
        const cross = getCross(targetSeed, value)
        cross.label = relation.label
        cross.type = 'hasOne'
        ret.crosses[columnOrCross] = cross
      }

      if(belongsTo && belongsTo[columnOrCross]) {
        const relation = belongsTo[columnOrCross]
        const targetSeed = relation.targetSeed
        const cross = getCross(targetSeed, value)
        cross.label = relation.label
        cross.type = 'belongsTo'
        ret.crosses[columnOrCross] = cross
      }

      if(hasMany && hasMany[columnOrCross]) {
        const relation = hasMany[columnOrCross]
        const targetSeed = relation.targetSeed
        const cross = getCross(targetSeed, value)
        cross.label = relation.label
        cross.type = 'hasMany'
        ret.crosses[columnOrCross] = cross
      }

      if(habtm && habtm[columnOrCross]) {
        const relation = habtm[columnOrCross]
        const targetSeed = relation.targetSeed
        const cross = getCross(targetSeed, value)
        cross.label = relation.label
        cross.type = 'habtm'
        ret.crosses[columnOrCross] = cross
      }
    } else {
      const columnName = snakeCase(columnOrCross)
      // const column = mergeActionElements(actionElements, seed.columns[columnName])
      const column = seed.columns[columnName]
      ret.columns[columnName] = column
      ret.rules[columnName] = getValidates(column)
    }
  }

  return ret
}

export function getDeclares(action: Action) {
  const seed = action.seed
  const declares = action.declares || seed.declares
  if(!declares) return Object.keys(seed.columns).filter(columnName => columnName !== 'id')

  const ret: string[] = []
  for(const i in declares) ret.push(...declares[i].split(',').map(e => e.trim()))

  return Array.from(new Set(ret)).filter(columnName => columnName !== 'id')
}

export function getSearches(action: Action) {
  const seed = action.seed
  const searches = action.searches || seed.searches
  if(!searches) return []

  const ret: string[] = []
  for(const i in searches) ret.push(...searches[i].split(',').map(e => e.trim()))

  return Array.from(new Set(ret))
}

export function getOrders(action: Action) {
  const seed = action.seed
  const orders = action.orders || seed.orders
  if(!orders) return []

  const orderList = orders as string[]
  const ret: string[] = []
  for(const i in orderList) ret.push(...orderList[i].split(',').map(e => e.trim()))

  return Array.from(new Set(ret))
}

export const getColumnNames = (seed: Seed) => {
  return Object.keys(seed.columns)
}

/**
 * 将申明列表构造成对象
 * @param declares 如：[ id, name_zh, country.name_zh, country.category.name_zh, country.category.name_zht ]
 * @return 构造成对象
 */
export function buildDeclared(seed: Seed, declares: string[]): Declared {
  const ret: Declared = {}
  
  const crossMap: Record<string, string[]> = {}
  for(const i in declares) {
    let declare = declares[i]
    if(declare.includes('.')) {
      const pos = declare.indexOf('.')
      const cross = declare.substring(0, pos)
      const value = declare.substring(pos + 1)
      let list = crossMap[cross] || []
      list.push(value)
      crossMap[cross] = list
    } else {
      if('*' == declare) {
        for(const columnName in seed.columns) ret[columnName] = undefined
      } else {
        const column = seed.columns[snakeCase(declare)]
        column ? ret[snakeCase(declare)] = undefined : crossMap[declare] = ['*']
      }
    }
  }

  for(const cross in crossMap) {
    const relation = getRelation(seed, cross)
    ret[cross] = buildDeclared(relation.targetSeed, crossMap[cross])
  }

  return ret;
}

export function getProperty(seed: Seed, crossOrColumn: string): Property {
  if(seed.columns && seed.columns[crossOrColumn]) {
    return {type: 'column', value: seed.columns[crossOrColumn]}
  }

  if(seed.belongsTo && seed.belongsTo[crossOrColumn]) {
    return {type: 'belongsTo', value: seed.belongsTo[crossOrColumn]}
  }

  if(seed.hasOne && seed.hasOne[crossOrColumn]) {
    return {type: 'hasOne', value: seed.hasOne[crossOrColumn]}
  }

  if(seed.hasMany && seed.hasMany[crossOrColumn]) {
    return {type: 'hasMany', value: seed.hasMany[crossOrColumn]}
  }

  if(seed.habtm && seed.habtm[crossOrColumn]) {
    return {type: 'habtm', value: seed.habtm[crossOrColumn]}
  }

  throw Error(`【${seed.name}.${crossOrColumn}】不存在`)
}


export function getInferSelfColumn(relation: Relation, type: RelationType ) {
  if(relation.selfColumn) return relation.selfColumn

  if(type == 'belongsTo') return `${relation.targetSeed.name}_id`
  return 'id'
}

export function getInferTargetColumn(relation: Relation, type: RelationType ) {
  if(relation.targetColumn) return relation.targetColumn

  if(type == 'hasOne' || type == 'hasMany') return `${relation.selfSeed.name}_id`
  return 'id'
}

export function getInferThroughSelfColumn(habtm: Habtm ) {
  if(habtm.throughSelfColumn) return habtm.throughSelfColumn

  return `${habtm.selfSeed.name}_id`
}

export function getInferThroughTargetColumn(habtm: Habtm ) {
  if(habtm.throughTargetColumn) return habtm.throughTargetColumn

  return `${habtm.targetSeed.name}_id`
}

export function getRefColumn(seed: Seed, ...excludeColumnNames: string[]) {
  const columns = seed.columns;
  if(!columns) return undefined;

  const refColumns = [];
  for(const columnName in columns) {
    const column = columns[columnName]
    if(column.type != 'ref' || excludeColumnNames.includes(columnName)) continue

    refColumns.push(columns[columnName]);
  }

  if(refColumns.length > 1) throw Error(`目标【${seed.name}】出现【${refColumns.length}】个其它引用列, 有歧义`)

  // 只有一列，并且是引用类型的列，说明这个表是一个关系表
  return refColumns[0];
}

export function buildDeclaredTree(seed: Seed, declared: Declared, parent?: {value: string, label: string}) {
  const ret = []
  for(const crossOrColumn in declared) {
    const value = declared[crossOrColumn]
    if(!value) {
      const column = seed.columns[crossOrColumn]
      ret.push({column, value: `${parent ? parent.value + '.' : ''}${column.name}`, label: `${parent ? parent.label + '.' : ''}${column.label}`})
    } else {
      const relation = getRelation(seed, crossOrColumn)
      const targetSeed = relation.targetSeed
      const current = {value: `${parent ? parent.value + '.' : ''}${crossOrColumn}`, label: `${parent ? parent.label + '.' : ''}${relation.label}`}
      ret.push({relation, value: current, label: relation.label, children: buildDeclaredTree(targetSeed, value, current)})
    }
  }

  return ret
}

export function getSetColumnName(seed: Seed, action: Action) {
  const columnName = action.columnName;
  if(columnName) return columnName;

  let actionName = action.name;
  if(actionName.startsWith('get') || actionName.startsWith('set')) {
    actionName = lowerFirst(actionName.substring(3))
  }

  const s = snakeCase(actionName);
  if(Object.keys(seed.columns).includes(s) || Object.keys(getRelations(seed)).includes(s)) return s;

  throw new Error(`请为【${seed.name}.actions.${actionName}】配置【columnName】`);
}


// export function filterActions(actions: Record<string, Action>, ...positions: ActionPosition[]) {
//   const ret: Record<string, Action> = {}

//   if(!actions) return ret
//   if(positions.length == 0) return actions

//   for(const actionName in actions) {
//     const action = actions[actionName]
//     const position = action.position
//     if(!position) continue

//     const actionPositions = isArray(position) ? position as ActionPosition[] : [position as ActionPosition]
//     if(actionPositions.some(e => positions.includes(e))) ret[actionName] = action
//   }

//   return ret
// }

export function findActions(seed: Seed, actionNames: string) {
  const ret: Record<string, Action> = {}
  const actions = seed.actions
  if(!actionNames || !actions || Object.keys(actions).length == 0) return ret

  if(actionNames.startsWith('!')) {
    const excludeActionNames = actionNames.substring(1).split(',').map(e => e.trim())
    for(const actionName in actions) {
      const action = actions[actionName]
      if(action && !excludeActionNames.includes(actionName)) ret[actionName] = action
    }

    return ret
  }

  for(const actionName of actionNames.split(',').map(e => e.trim())) {
    const action = actions[actionName]
    if(action) ret[actionName] = action
  }

  return ret
}

export function getSeedNames(seeds: Record<string, Seed>, libSeeds?: Record<string, Seed>) {
  const ret: string[] = []
  if(seeds) {
    for(const seedName in seeds) {
      if(!ret.includes(seedName)) ret.push(seedName)
    }
  }
  if(libSeeds) {
    for(const seedName in libSeeds) {
      if(!ret.includes(seedName)) ret.push(seedName)
    }
  }

  return ret
}

export function getActionByType(seed: Seed, actionType: ActionType) {
  const actions = seed?.actions
  if(!actions) return void 0

  for(const actionName in actions) {
    const action = actions[actionName]
    const type = action.type
    if(!type) continue

    if(type == actionType) return action
  }

  return void 0
}

